#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Abstraction factory for file system access

import os
import StringIO
from boto import s3


class FileSystem(object):
    
    def OpenFile(self, name, mode):
        return StringIO.StringIO()

    def DeleteFile(self, name):
        pass


class RealFileSystem(FileSystem):

    def __init__(self, basedir=''):
        self.basedir_ = basedir
        
    def SetBaseDir(self, basedir):
        self.basedir_ = basedir.rstrip('/')

    def GetBaseDir(self):
        return self.basedir_
    
    def OpenFile(self, filename, mode):
        fullname = os.path.join(self.basedir_, filename)
        return open(fullname, mode)

    def DeleteFile(self, filename):
        fullname = os.path.join(self.basedir_, filename)
        os.remove(fullname)


class MockFileSystem(FileSystem):
    
    def __init__(self):
        self.files_ = {}

    def CreateNewClose(self, fullname, f):
        old_close = f.close
        def FakeClose():
            self.files_[fullname]['content'] = f.getvalue()
            old_close()
        return FakeClose
    
    def OpenFile(self, filename, mode):
        mfile = None
        if filename not in self.files_:
            mfile = StringIO.StringIO()
            mfile.close = self.CreateNewClose(filename, mfile)
            self.files_[filename] = {'mode': mode, 
                                     'content': ''}
        else:
            mfile = StringIO.StringIO(self.files_[filename]['content'])
            mfile.close = self.CreateNewClose(filename, mfile)
        return mfile

    def DeleteFile(self, filename):
        del self.files_[filename]


class S3FileSystem(FileSystem):
    
    def __init__(self, access_id='', access_key='', bucket_name=''):
        self.access_id_ = access_id
        self.access_key_ = access_key
        self.bucket_name_ = bucket_name
        self.conn_ = None
        self.bucket_ = None

    @property
    def access_id(self):
        return self.access_id_
    
    @access_id.setter
    def access_id(self, value):
        if type(value) in [str]:
            self.access_id_ = value
            self.conn_ = None
        else:
            raise (TypeError, 'Access ID must be a string')

    @property
    def access_key(self):
        return self.access_key_
    
    @access_key.setter
    def access_key(self, value):
        if type(value) in [str]:
            self.access_key_ = value
            self.conn_ = None
        else:
            raise (TypeError, 'Access Key must be a string')

    @property
    def bucket_name(self):
        return self.bucket_name_
    
    @bucket_name.setter
    def bucket_name(self, value):
        if type(value) in [str]:
            self.bucket_name_ = value
            self.conn_ = None
        else:
            raise (TypeError, 'Bucket name must be a string')

    def check_conn_(self):
        if not self.conn_:
            self.conn_ = s3.connection.S3Connection(
                self.access_id_, self.access_key_)
            try:
                self.bucket_ = self.conn_.get_bucket(self.bucket_name_)
            except s3.bucket.S3ResponseError:
                self.bucket_ = self.conn_.create_bucket(self.bucket_name_)


    def CreateNewClose(self, fullname, f):
        old_close = f.close
        def FakeClose():
            self.check_conn_()
            key = s3.key.Key(self.bucket_)
            key.key = fullname
            key.set_contents_from_string(f.getvalue())
            old_close()
        return FakeClose
    
    def OpenFile(self, filename, mode):
        self.check_conn_()
        mfile = None
        key = self.bucket_.get_key(filename) 
        if not key: 
            mfile = StringIO.StringIO()
            mfile.close = self.CreateNewClose(filename, mfile)
        else:
            mfile = StringIO.StringIO(
                key.get_contents_as_string())
            mfile.close = self.CreateNewClose(filename, mfile)
        return mfile

    def DeleteFile(self, filename):
        self.check_conn_()
        self.bucket_.delete_key(filename)

# File System Abstraction Factory
def FileSystemAF(name, *args, **kargs):
    fs = {'realfs': RealFileSystem,
          'mockfs': MockFileSystem,
          's3fs': S3FileSystem,
          'nonefs': FileSystem}
    if name in fs:
        return fs[name](*args, **kargs)
    else:
        raise(ValueError, 'Inexistent Filesystem')
        
        
